#ifndef	__N_MAP_CONFIG_HPP_2010__
#define	__N_MAP_CONFIG_HPP_2010__

#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <errno.h>

#include <map>
#include <set>
#include <vector>
#include <stdexcept>


/**********************************************************************
 *

 Examples :

#include "boque/nmapconfig.hpp"

const char* INI_FILENAME = "nmap.conf";
const char* INI_SECTION  = "fish";

// initialize
NMapConfig*  nmc_p = NULL;
try {
	nmc_p = new NMapConfig( INI_FILENAME, INI_SECTION );
}
catch( std::exception& e ) {
	// something is wrong. see e.what()
	std::cerr << "Exception -- " << e.what();
	exit( 1 );
}
 
// get lower bound of ID in configuration
int	low_bound = nmc_p->lowbound();
std::cout << "lower bound is " << low_bound << std::endl;

// get upper bound of ID in configuration
int	up_bound = nmc_p->upbound();
std::cout << "upper bound is " << up_bound << std::endl;

// lookup
int	id = 1234;
NMapConfig::target_list_ptr_type    targets_p = NULL;
targets_p = nmc_p->lookup( id );
if ( NULL == targets_p ) {
	// NOT FOUND
	exit( 2 );
}
if ( targets_p->size() < 1 ) {
	// NO TARGETS
	exit( 3 );
}
for ( unsigned x=0; x < targets_p->size(); ++x ) {
	const std::string*	name_p = targets_p->at( x );
	fprintf( stderr, "    ID=%d #%u --> %s\n", id, x, name_p->c_str() );
}

// finalize
delete nmc_p;
nmc_p = NULL;




Smaple Configure File Content :

[fish]

map.max = 1024

map.rule.fish://192.168.0.12:4705 = 1 - 512
map.rule.fish://192.168.0.13:4705 = 513 - 1024


[bird]

map.max = 4096

map.rule.fish://192.168.0.16:4705 = 1 - 1024
map.rule.fish://192.168.0.17:4705 = 1025 - 2048
map.rule.fish://192.168.0.18:4705 = 2049 - 3072
map.rule.fish://192.168.0.19:4705 = 3073 - 4096

##### END OF CONFIGURE ######

 **********************************************************************/


class	NMapConfig
{
	#define	_MAX_TARGET_ID	1048576

public	:
	typedef	std::vector< const std::string * >	target_list_type;
	typedef	target_list_type*	target_list_ptr_type;

private	:
	typedef	std::set< std::string >	set_target_name_type;
	typedef	std::vector< target_list_type >		target_map_type;
	
public	:
	NMapConfig( const char* ini_filename, const char* ini_section = NULL )
	{
		_lower_bound = _MAX_TARGET_ID;
		_upper_bound = 0;

		loadConfig( ini_filename, ini_section );
	}

public	:

	const target_list_ptr_type	lookup( int id )
	{
		if ( ( id < 0 ) || ( id > _max_target_id ) ) {
			return	NULL;
		}

		return	&(_targets_map[ id ]);
	}

	int	lowbound( void ) { return _lower_bound; }
	const int lowbound( void ) const { return _lower_bound; }
	int upbound( void )  { return _upper_bound; }
	const int upbound( void )  const { return _upper_bound; }

private	:

	void	loadConfig( const char* ini_filename, const char* ini_section )
	{
		char    szLine[ 1024 ];

		if (NULL == ini_filename ) {
			memset( szLine, 0, sizeof(szLine) );
			snprintf( szLine, sizeof(szLine) - 1, "No INI filename" );
			throw std::runtime_error( szLine );
		}

		FILE*	fp = fopen( ini_filename, "r" );
		if ( NULL == fp ) {
			memset( szLine, 0, sizeof(szLine) );
			snprintf( szLine, sizeof(szLine) - 1, "fail to open INI filename '%s' -- %s'", ini_filename, strerror(errno) );
			throw std::runtime_error( szLine );
		}
		_max_target_id = 0;

		bool	will_match_section = false;
		bool	section_matched = false;
		char	section_watched[ 64 ] = { 0 };
		if ( NULL != ini_section ) {
			snprintf(section_watched, sizeof(section_watched) - 1, "[%s]", ini_section );
			will_match_section = true;
		}

		const char*     pszMapRulePrefix = "map.rule.";
		int     iSizeMapRulePrefix = strlen( pszMapRulePrefix );

		while ( !feof(fp) ) {
			memset ( szLine, 0, sizeof(szLine) );
			fgets ( szLine, sizeof(szLine), fp );
			if ( (trim (szLine) < 2) || ('#' == szLine[0]) )
				continue;

			if ( will_match_section ) {
				if ('[' == szLine[0]) {
					if ( 0 == strcmp(szLine, section_watched) ) {
						section_matched = true;
					}
					else {
						section_matched = false;
					}
				}

				if ( false == section_matched ) {
					// just ignore
					continue;
				}
			}

			char    *pszKey = szLine, *pszValue = szLine, *p = szLine;

			while ( ('=' != *p) && ('\0' != *p) ) p++;
			if ( '\0' == *p )
				continue;
			*p = '\0';
			pszValue = ++p;

			while ( ('#' != *p) && ('\0' != *p) ) p++;
			if ( '#' == *p )
				*p = '\0';

			trim ( pszKey ); trim ( pszValue );
			//fprintf ( stdout, "key='%s' value='%s'\n", pszKey, pszValue );

			// get the Maximum Map ID
			if (0 == strcmp("map.max", pszKey))
			{
				int     _iMaxMap = atoi ( pszValue );
				if ( _iMaxMap > _MAX_TARGET_ID ) {
					memset( szLine, 0, sizeof(szLine) );
					snprintf( szLine, sizeof(szLine) - 1, "Invalid Maximum of map ( out of [ 1 .. %d ]", _MAX_TARGET_ID );
					throw std::runtime_error( szLine );
				}

				for (int i = 0; i <= _iMaxMap; i++)
				{
					std::vector< const std::string * >      _tmp;
					_targets_map.push_back ( _tmp );
				}
				_max_target_id = _iMaxMap;

				continue;
			}

			// whether is a map.rule
			if (0 != strncmp(pszMapRulePrefix, pszKey, iSizeMapRulePrefix))
			{
				// have nothing to do with map.rule
				continue;
			}

			// get key group address
			std::string strKey ( pszKey + iSizeMapRulePrefix  );
			set_target_name_type::iterator  result = _targets_name.find ( strKey );
			if (_targets_name.end() == result)
			{
				_targets_name.insert ( strKey );
				result = _targets_name.find ( strKey );
			}
			const std::string * target_p = &(*result);

			//INFO ( CAT_NMAP, "KEY:%s, pszTarget:%s", strKey.c_str(), pszTarget );

			// get the range of key
			char*   pszLowBound = pszValue;
			char*   pszUpBound = pszValue;
			p = pszValue;
			while ( ('-' != *p) && ('\0' != *p) ) p++;
			if ( '-' == *p ) {
				*p = '\0';
				pszUpBound = p + 1;
			}
			int     iLowBound = atoi ( pszLowBound );
			int     iUpBound = atoi ( pszUpBound );

			if (iUpBound < iLowBound) {
				int	tmp = iLowBound;
				iLowBound = iUpBound;
				iUpBound = tmp;
			}
			if ( (iLowBound < 0) || (iUpBound > _MAX_TARGET_ID) || (iUpBound > _max_target_id) ) {
				memset( szLine, 0, sizeof(szLine) );
				snprintf( szLine, sizeof(szLine) - 1, "Invalid bound[%d .. %d] of map ( out of [ 1 .. %d ]", iLowBound, iUpBound, _max_target_id );
				throw std::runtime_error( szLine );
			}
			if ( iLowBound < _lower_bound ) {
				_lower_bound = iLowBound;
			}
			if ( iUpBound > _upper_bound ) {
				_upper_bound = iUpBound;
			}


			while (_max_target_id < iUpBound)
			{
				std::vector< const std::string * >      _tmp;
				_targets_map.push_back ( _tmp );

				++_max_target_id;
			}

			// map key to group name
			for (int i = iLowBound; i <= iUpBound ; i++)
			{
				std::vector< const std::string * >& _targets = _targets_map[ i ];
				_targets.push_back ( target_p );
			}
		}

		// fill default slot
		while ( _targets_map.size() <= (_max_target_id + 1) ) {
			std::vector< const std::string * >      _tmp;
			_targets_map.push_back ( _tmp );
		}
		if ( _upper_bound < _max_target_id ) {
			_upper_bound = _max_target_id;
		}

		fclose ( fp );

		if ( (_upper_bound < 1) || (_max_target_id < 1) ) {
			memset( szLine, 0, sizeof(szLine) );
			snprintf( szLine, sizeof(szLine) - 1, "bad configure in [%s] of %s", ini_section, ini_filename );
			throw std::runtime_error( szLine );
		}
	}


	int 	trim ( char* pszString )
	{
		char*   p = pszString;
		if ( NULL == p ) { 
			return  0;  
		}   

		char*   q = pszString;
		char*   e = p + strlen(p) - 1;
		while ((' ' == *p) || ('\t' == *p) || ('\r' == *p) || ('\n' == *p)) p++;
		while ((p < e) && ((' ' == *e) || ('\t' == *e) || ('\r' == *e) || ('\n' == *e))) e--;

		int     len = e - p + 1;
		while (p <= e) *q++ = *p++;
		*q = '\0';

		return  len;
	}

private	:
	NMapConfig( const NMapConfig& );
	NMapConfig& operator=( const NMapConfig& );

private	:

	set_target_name_type	_targets_name;
	
	target_map_type		_targets_map;

	uint32_t	_max_target_id;

	int		_lower_bound;
	int		_upper_bound;
};	//// class NMapConfig


#endif	// __N_MAP_CONFIG_HPP_2010__

